package de.softwareforge.published.util;

/*
 * Copyright (C) 2007-2008 Henning P. Schmiedehausen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import de.softwareforge.published.doclet.PublishedDoclet;


/**
 * Represents the actual publishing status for a single element. Calculates the visibility depending on the PublishedInfo object
 * that is part of the status. Every element must have its own instance of this object.
 *
 * @author <a href="mailto:henning@schmiedehausen.org">Henning P. Schmiedehausen</a>
 * @version $Id:PublishedStatus.java 357 2007-08-13 20:57:20Z hps@intermeta.de $
 * @api.level unpublished
 */
public final class PublishedStatus
{
    private final PublishedDoclet doclet;

    private PublishInformation info = null;

    private boolean isReferenced = false;

    private boolean inProgress = false;

    private boolean finalState = false;

    private boolean hasTag = false;

    /**
     * Creates a new PublishedStatus object.
     *
     * @param doclet The current Doclet configuration object.
     */
    public PublishedStatus(final PublishedDoclet doclet)
    {
        this(doclet, true);
    }

    /**
     * Creates a new PublishedStatus object.
     *
     * @param doclet The current Doclet configuration object.
     * @param finalState If true, this object is considered "final state", it was created or cloned during the regular tree parsing.
     *                   If it gets created when visiting annotation elements, this flag must be false.
     */
    public PublishedStatus(final PublishedDoclet doclet, final boolean finalState)
    {
        this.doclet = doclet;
        this.info = doclet.getConfiguration().getDefaultPublishLevel();
        this.finalState = finalState;
    }

    private PublishedStatus(final PublishedStatus status)
    {
        this.doclet = status.doclet;
        this.info = status.info;
        this.isReferenced = status.isReferenced;
        this.inProgress = status.inProgress;
        this.finalState = status.finalState;
        this.hasTag = status.hasTag; // This should not be cloned, because it might not be correct.
    }

    /**
     * Creates a new PublishedStatus object.
     *
     * @return creates a new PublishedStatus object.
     */
    @Override
    public synchronized PublishedStatus clone()
    {
        return new PublishedStatus(this);
    }

    /**
     * If this flag is set, then the element has been referenced from another JavaDoc and should be made visible.
     *
     * @param isReferenced True if this element should be made visible.
     */
    public void setIsReferenced(final boolean isReferenced)
    {
        this.isReferenced = isReferenced;
    }

    /**
     * Returns true if the Javadoc element represented by this status object should be visible. A Javadoc element becomes visible if
     *
     * <ul>
     *   <li>it has been referenced from a visible object.</li>
     *   <li>it has no publishing level at all and unpublished elements should be displayed</li>
     *   <li>it has a publishing level that is equal or higher to the requested display level</li>
     * </ul>
     *
     * @return True if the Javadoc element should be displayed.
     */
    public boolean isVisible()
    {

        if (isReferenced || doclet.getConfiguration().isIgnoreTags() || doclet.getConfiguration().isPublishAll())
        {
            return true;
        }

        // Do we have an explicit level set? If not, use the default. If
        // we should hide unpublished elements, return false.
        if (info == null)
        {
            return !doclet.getConfiguration().isHideUntagged();
        }
        else
        {
            return info.getPrio() >= doclet.getConfiguration().getPublishLevel().getPrio();
        }
    }

    /**
     * Sets the value of the publish information property.
     *
     * @param info The new publish information value.
     */
    public void setPublishInformation(final PublishInformation info)
    {
        this.info = info;
    }

    /**
     * Sets the value of the publish information property.
     *
     * @param status The new publish information value, derived from the given {@link PublishedStatus} object.
     */
    public void setPublishInformation(final PublishedStatus status)
    {
        this.info = status.info;
    }

    /**
     * Returns the value of the publish information property.
     *
     * @return The current value of the publish information property. Can be null.
     */
    public PublishInformation getPublishInformation()
    {
        return info;
    }

    /**
     * Returns the value of the in progress property.
     *
     * @return The current value of the in progress property.
     */
    public boolean isInProgress()
    {
        return this.inProgress;
    }

    /**
     * Sets the value of the in progress property.
     *
     * @param inProgress The new in progress value.
     */
    public void setInProgress(final boolean inProgress)
    {
        this.inProgress = inProgress;
    }

    /**
     * Returns the value of the final state property.
     *
     * @return The current value of the final state property.
     */
    public boolean isFinalState()
    {
        return this.finalState;
    }

    /**
     * Sets the value of the final state property.
     *
     * @param finalState The new final state value.
     */
    public void setFinalState(boolean finalState)
    {
        this.finalState = finalState;
    }

    /**
     * True when the document represented by this Status object has an api.level tag.
     *
     * @return True if the document has an api.level tag.
     */
    public boolean hasTag()
    {
        return hasTag;
    }

    /**
     * Sets the value of the has tag property.
     *
     * @param hasTag The new has tag value.
     */
    public void setHasTag(final boolean hasTag)
    {
        this.hasTag = hasTag;
    }

    /**
     * @see Object#toString()
     */
    @Override
    public String toString()
    {
        return "/publish info: " + ((info == null) ? "<unset>" : info)
                + " /in progress: " + isInProgress()
                + " /visible: " + isVisible()
                + " /finalState: " + isFinalState()
                + " /has tag: " + hasTag();
    }
}
